package top.amot.forceview.force

import top.amot.forceview.layout.I_Layout
import top.amot.forceview.model.AbstractNode
import kotlin.math.sqrt

class ForceRadial : AbstractForce {
    private var radius: NodeCalculation? = null
    private var strength: NodeCalculation? = null
    private var x = 0.0f
    private var y = 0.0f
    private lateinit var radiuses: FloatArray
    private lateinit var strengths: FloatArray

    constructor() {}
    constructor(x: Float, y: Float) {
        this.x = x
        this.y = y
    }

    override fun initialize(layout: I_Layout) {
        super.initialize(layout)
        initialize()
    }

    override fun apply(alpha: Float) {
        nodes.forEachIndexed { i, node ->
            var dx = node.x - x
            var dy = node.y - y
            dx = if (dx == 0.0f) 1.0E-6F else dx
            dy = if (dy == 0.0f) 1.0E-6F else dy
            val r = sqrt(dx * dx + dy * dy)
            val k = (radiuses[i] - r) * strengths[i] * alpha / r
            node.vx += dx * k
            node.vy += dy * k
        }
    }

    fun radius(c: NodeCalculation?): ForceRadial {
        radius = c
        return this
    }

    fun strength(c: NodeCalculation?): ForceRadial {
        strength = c
        return this
    }

    fun x(x: Float): ForceRadial {
        this.x = x
        return this
    }

    fun y(y: Float): ForceRadial {
        this.y = y
        return this
    }

    private fun initialize() {
        strengths = FloatArray(nodes.size)
        radiuses = FloatArray(nodes.size)
        for (i in nodes.indices) {
            radiuses[i] = radius(nodes[i])
            strengths[i] = strength(nodes[i])
        }
    }

    private fun radius(node: AbstractNode): Float {
        val r = radius?.calculateNode(node)?:DEFAULT_RADIUS

        return when{
            r < 0  -> -r
            r > 0  -> r
            else   -> DEFAULT_RADIUS
        }
    }

    private fun strength(node: AbstractNode): Float {
        return strength?.calculateNode(node)?:0f

    }

    companion object {
        const val NAME = "Radial"
        private const val DEFAULT_RADIUS = 1.0f
    }
}